package com.sparrow.rpc.future;

import com.sparrow.rpc.service.RpcService;
import com.sparrow.rpc.callback.RpcCallback;
import com.sparrow.rpc.exception.RpcTimeOutException;
import com.sparrow.rpc.socket.msg.resp.RpcResponse;
import io.netty.util.Timeout;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * RpcFuture 类用于表示一个异步 RPC 调用的未来结果。
 * 它提供了获取结果、设置超时、添加回调等功能，支持同步和异步处理 RPC 调用的结果。
 *
 * @param <T> 结果的类型
 */
public class RpcFuture<T> {

    // 默认的超时时间，单位为秒
    private static final int DEFAULT_TIME_OUT_SEC = 15;

    // 请求的唯一标识符
    private long requestId;
    // 方法的相关信息
    private String methodInfo;

    private RpcResponse response;
    // RPC 调用的结果
    private T result;
    // RPC 调用过程中出现的错误
    protected Throwable error;
    // 存储 RPC 回调的列表
    private final List<RpcCallback<T>> callbacks = new ArrayList<>();
    // 超时时间，单位为秒
    private int timeOutSec = DEFAULT_TIME_OUT_SEC;
    // Netty 的超时任务
    private Timeout timeOut;
    // 用于同步等待结果的计数器
    private final CountDownLatch countDownLatch = new CountDownLatch(1);

    /**
     * 静态工厂方法，创建一个已完成的 RpcFuture 实例。
     *
     * @param s 结果值
     * @param <T> 结果的类型
     * @return 已完成的 RpcFuture 实例
     */
    public static <T> RpcFuture<T> of(T s) {
        RpcFuture<T> future = new RpcFuture<>();
        future.result = s;
        return future;
    }

    /**
     * 设置请求的唯一标识符。
     *
     * @param requestId 请求的唯一标识符
     */
    public void setRequestId(long requestId) {
        this.requestId = requestId;
    }

    /**
     * 获取请求的唯一标识符。
     *
     * @return 请求的唯一标识符
     */
    public long getRequestId() {
        return requestId;
    }

    /**
     * 设置超时时间，并重新添加超时任务。
     *
     * @param timeOutSec 超时时间，单位为秒
     */
    public void setTimeOutSec(int timeOutSec) {
        this.timeOutSec = timeOutSec;
        RpcService.addTimeOut(this);
    }

    /**
     * 获取 RPC 调用的结果。
     *
     * @return RPC 调用的结果
     */
    public T getResult() {
        return result;
    }

    /**
     * 设置 RPC 调用的结果。
     *
     * @param result RPC 调用的结果
     */
    public void setResult(T result) {
        this.result = result;
    }

    /**
     * 添加一个 RPC 回调，用于处理结果或错误。
     *
     * @param callback RPC 回调
     */
    public synchronized void addCallback(RpcCallback<T> callback) {
        if (this.result != null) {
            completeWithResult(this.result);
        } else if (this.error != null) {
            completeWithError(this.error);
        } else {
            this.callbacks.add(callback);
        }
    }

    /**
     * 同步等待指定时间获取 RPC 调用的结果。
     * 如果在等待时间内结果未返回或出现错误，将抛出相应的异常。
     *
     * @param waitTime 等待时间
     * @param unit 时间单位
     * @return RPC 调用的结果
     * @throws Throwable 如果出现错误，将抛出相应的异常
     */
    public T sync(long waitTime, TimeUnit unit) throws Throwable {
        try {
            countDownLatch.await(waitTime, unit);
            if (this.error != null) {
                throw this.error;
            }
            return result;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw e;
        }
    }

    /**
     * 获取超时时间。
     *
     * @return 超时时间，单位为秒
     */
    public int getTimeOutSec() {
        return timeOutSec;
    }

    /**
     * 处理超时事件，将错误信息传递给回调并减少计数器。
     */
    public void onTimeOut() {
        completeWithError(new RpcTimeOutException("rpc执行超时 " + this));
    }

    /**
     * 设置 Netty 的超时任务。
     *
     * @param timeout Netty 的超时任务
     */
    public void setTimeOutTask(Timeout timeout) {
        this.timeOut = timeout;
    }

    /**
     * 处理 RPC 响应，根据响应结果调用相应的处理方法。
     *
     * @param response RPC 响应
     */
    public synchronized void complete(RpcResponse response) {
        if (response.getError() != null) {
            completeWithError(new RuntimeException(response.getError()));
        } else {
            completeWithResult(response.getResult());
        }
    }

    /**
     * 处理成功的 RPC 结果，将结果传递给回调并减少计数器。
     */
    private synchronized void completeWithResult(Object resultObj) {
        cancelTimeOutTask();
        if (resultObj != null) {
            try {
                this.result = (T) resultObj;
            } catch (ClassCastException e) {
                completeWithError(new RuntimeException("结果类型转换错误", e));
                return;
            }
        }
        for (RpcCallback<T> callback : callbacks) {
            callback.onResponse(this.result);
        }
        callbacks.clear();
        countDownLatch.countDown();
    }

    /**
     * 处理 RPC 调用过程中出现的错误，将错误信息传递给回调并减少计数器。
     *
     * @param error 错误信息
     */
    private synchronized void completeWithError(Throwable error) {
        cancelTimeOutTask();
        this.error = error;
        for (RpcCallback<T> callback : callbacks) {
            callback.onError(this.error);
        }
        callbacks.clear();
        countDownLatch.countDown();
    }

    /**
     * 设置方法的相关信息。
     *
     * @param methodInfo 方法的相关信息
     */
    public void setMethodInfo(String methodInfo) {
        this.methodInfo = methodInfo;
    }

    /**
     * 获取方法的相关信息。
     *
     * @return 方法的相关信息
     */
    public String getMethodInfo() {
        return methodInfo;
    }

    /**
     * 取消 Netty 的超时任务。
     */
    public void cancelTimeOutTask() {
        if (this.timeOut != null) {
            this.timeOut.cancel();
            this.timeOut = null;
        }
    }

    /**
     * 重写 toString 方法，方便调试和日志记录。
     *
     * @return 包含 RpcFuture 信息的字符串
     */
    @Override
    public String toString() {
        return "RpcFuture{" +
                "requestId=" + requestId +
                ", methodInfo='" + methodInfo + '\'' +
                '}';
    }
}